feat: Add methods to store & restore UI component state - #3208
Conversation
Add to_component_instances() and from_components() to View and DesignerView for component-instance state round-tripping.
When importing ActionRow component instances, assign the source row index to items before adding them so exported View layouts round-trip accurately.
Add View and DesignerView tests for to_component_instances()/from_components().
|
Thanks for opening this pull request! This pull request can be checked-out with: git fetch origin pull/3208/head:pr-3208
git checkout pr-3208This pull request can be installed with: pip install git+https://github.com/Pycord-Development/pycord@refs/pull/3208/head |
Co-authored-by: Paillat <jeremiecotti@ik.me> Signed-off-by: Toby <83095177+tobezdev@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a way to snapshot and restore discord.ui view state using component instances (rather than raw payload dicts), addressing the need described in #2385.
Changes:
- Add
to_component_instances()toViewandDesignerViewfor exporting in-memoryComponentinstances. - Add
from_components()toViewandDesignerViewfor restoring views from component instances (with row preservation for legacyViewActionRows). - Add unit tests covering round-tripping for both
ViewandDesignerView.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
discord/ui/view.py |
Introduces component-instance export/import APIs for View and DesignerView. |
tests/ui/test_view_component_instances.py |
Adds round-trip tests for the new APIs and confirms existing dict-based behavior remains unchanged. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
NeloBlivion
left a comment
There was a problem hiding this comment.
Misc.
- This should target the base
ItemInterfaceclass to support modals as well. - I'm not sure if a testing implementation is necessary here considering we do not have any tests implemented besides voice
- This should be considered after #3093 is merged
Add to_component_instances() method which returns the underlying instances from a list of components
to_component_instances() now targets the shared base layer & added on ItemInterface using each item’s _generate_underlying(), and exposed from BaseView so DesignerView no longer duplicates it
Adjusted _generate_underlying() to allow already-backed items and allows usage on ItemInterface
Nelo asked for support for `ItemInterface` so dont ask me ok
Signed-off-by: Lala Sabathil <lala@pycord.dev>
This comment was marked as resolved.
This comment was marked as resolved.
|
|
||
| ### Added | ||
|
|
||
| - Methods to store and restore states of UI components. |
There was a problem hiding this comment.
| - Methods to store and restore states of UI components. | |
| - Added `View.to_component_instances()`/`from_components()` for snapshotting and | |
| restoring UI component state. |
vmphase
left a comment
There was a problem hiding this comment.
The test failure is caused by a test-ordering effect. pytest-asyncio tears down a function-scoped loop after each async test, leaving _set_called=True and _loop=None. That's why asyncio.get_event_loop() raises a RuntimeError in the later sync voice tests instead of auto-creating a loop.
Bulk-accept my requested changes to add an autouse fixture that resets the loop state after every test, and this will be good to go.
P.S.: Ideally, this fixture belongs into conftest.py file. Since we don't have one yet and this is yet the only file with async tests, it's fine to leave it here for now. I will move it within a separate cleanup PR later.
|
|
||
| import pytest |
There was a problem hiding this comment.
| import pytest | |
| import asyncio | |
| import pytest |
| from discord.components import InputText as InputTextComponent | ||
|
|
||
|
|
||
| @pytest.mark.asyncio |
There was a problem hiding this comment.
| from discord.components import InputText as InputTextComponent | |
| @pytest.mark.asyncio | |
| from discord.components import InputText as InputTextComponent | |
| @pytest.fixture(autouse=True) | |
| def _restore_event_loop_after_async_tests(): | |
| yield | |
| asyncio.set_event_loop(asyncio.new_event_loop()) | |
| @pytest.mark.asyncio |
Summary
Addresses issue #2385 by adding methods to store and restore UI component states via new methods
ui.View.to_component_instances()&ui.View.from_components(). The methodView.from_components()has also been edited to preserve row indexes by usingenumerate()rather than_walk_all_components().Note: I have intentionally not updated any formal documentation (markdown files) as I do not believe this is required. Any new functions have docstrings explaining their exact behaviour (and their code is very readable anyways). Anything existing has not been fundamentally changed so there will be no difference in terms of how people will use those methods. Please let me know if a documentation change is required and I will be happy to add this into the PR.
The Copilot CLI was used in the development of this PR, although no code was generated by it. It was used simply to identify an issue on this repo that I could have an attempt at using the GitHub MCP server, and I used it to understand the structure and standards of the already-existing code (for example, the format of docstrings).
Information
examples, ...).
Checklist
type: ignorecomments were used, a comment is also left explaining why.